2024-08-05
最后编辑于:2024-09-15
创建与销毁
lua_State* L = luaL_newstate();
lua_close(L);
function add(a, b)
return a + b
end
// 将函数推至栈顶
lua_getglobal(L, "add")
// 传参
lua_pushnumber(L, a);
lua_pushnumber(L, b);
// 调用
lua_call(L, 2, 1);
// 获取返回值
lua_Number c = lua_tonumber(L, -1);
int add(lua_State* L) {
lua_Number a = lua_tonumber(L, -2);
lua_Number b = lua_tonumber(L, -1);
lua_Number c = a + b;
lua_pushnumber(L, c);
}
add(1, 2)
lua_pushcfunction(L, add);
lua_setglobal(L, "add");
注册函数的语法糖
lua_register(L, "add", add);
这个宏实际是对上面两个调用的简写
例如
x = { name = "w6rsty", age = 18 }
获取字段
lua_getglobal(L, "x");
lua_pushtring(L, "name");
lua_gettable(L, -2);
const char* name = lua_tostring(L, -1);
// 注意push的string键还在栈上,继续访问需要先pop
lua_getglobal(L, "x");
lush_pushstring(L, "age");
lua_gettable(L, -2);
lua_Number age = lua_tonumber(L, -1);
lua CAPI也提供了语法糖
lua_getglobal(L, "x");
lua_getfield(L, -1, "name");
写入字段
lua_getglobal(L, "x");
lua_pushstring(L, "coding");
lua_setfield(L, -2, "status");
我们想要实现这样的操作
v1 = CreateVector()
v2 = CreateVector()
v1.x = 12
v1.y = 31
v3 = v1 + v3
其中v1,v2,v3是table
通过定义MetaTable和MetaMethods,可以进行table间的操作
struct Vector {
static int Create(lua_State* L) {
lua_newtable(L);
lua_pushstring(L, "x");
lua_pushnumber(L, 0);
lua_settable(L, -3);
// y
luaL_getmetatable(L, "VectorMetaTable");
lua_setmetatable(L, -2);
return 1;
}
static int __add(lua_State* L) {
lua_pushstring(L, "x");
lua_gettable(L, -3);
lua_Number xLeft = lua_tonumber(L, -1);
lua_pop(L, 1);
// yLeft
// xRight
// yRight
Vector::Create(L);
lua_pushstring(L, "x");
lua_pushnumber(L, xLeft + xRight);
lua_rawset(L, -3);
lua_pushstring(L, "y");
lua_pushnumber(L, yLeft + yRight);
lua_rawset(L, -3);
return 1;
}
}
此处在创建新的Vector
时使用lua_rawset
,不会在metatable中查找,避免循环调用
lua_pushcfunction(L, Vector::Create);
lua_setglobal(L, "CreateVector");
luaL_newmetatable(L, "VectorMetaTable");
lua_pushstring(L, "__add");
lua_pushcfunction(L, Vector::__add);
lua_settable(L, -3);
lua中metatable里特殊的键,名称使用__
开头
http://lua-users.org/wiki/MetatableEvents
要注意到lua_newuserdata
只是分配对应结构的大小,返回内存起始点的指针,为了调用构造函数,需要使用placement new
,在指针指向的位置进行构造
#include <new> //注意引入头文件
auto CreateSprite = [](lua_State* L) -> int {
void* ptr = lua_newuserdata(L, sizeof(Sprite));
new (ptr) Sprite();
return 1;
};
userdata类型数据的内存分配由lua负责,并使用gc进行回收 这也意味着在gc时不会主动调用类的析构函数,需要手动在lua回收时的metamethod中进行调用
在__gc
中调用析构
首先在创建时添加一个metatable,所有对象都共用这个表
auto CreateSprite = [](lua_State* L) -> int {
void* ptr = lua_newuserdata(L, sizeof(Sprite));
new (ptr) Sprite();
luaL_getmetatable(L, "SpriteMetaTable");
lua_setmetatable(L, -2);
return 1;
};
auto DestroySprite = [](lua_State* L) -> int {
Sprite* ptr = static_cast<Sprite*>(lua_touserdata(L, -1));
ptr->~Sprite();
return 0;
};
luaL_newmetatable(L, "SpriteMetaTable");
lua_pushstring(L, "__gc");
lua_pushcfunction(L, DestroySprite);
lua_settable(L, -3);